﻿package ascript
{
	//#########################
	// Header Includes
	//#########################
	import Box2D.Collision.Shapes.b2Shape;
	import Box2D.Common.Math.b2Vec2;
	import starling.text.TextField;
	import flash.utils.Dictionary;
	import starling.display.Sprite;
	import starling.events.Event;
	import ascript.utilities.*;
	import ascript.components.general.*;
	import ascript.components.input.*;
	import ascript.components.physics.*;
	import ascript.components.ai.*;
	import ascript.components.animation.*;
	import ascript.components.willard.*;
	import ascript.components.collision.*;
	import ascript.components.level.*;
	
	//#########################
	// Class Declaration
	//#########################
	public class GameObject extends Sprite
	{
		//=========================
		// Variables
		//=========================	
		// Privates
		protected var objectComponents:Dictionary = new Dictionary();
		
		//=========================
		// Constructors
		//=========================	
		public function GameObject( tName:String, tData:XML ):void
		{
			// Set Variables
			touchable = false; // default touchable off
			name = tName;
			objectAttributes[ "XML" ] = tData;
			objectAttributes[ "XML" ].ignoreWhitespace = true;
			objectAttributes[ "XMLArchetype" ] = new XML( ( ( ( game.gameAssets ).getAsset( "XMLArchetypes" ) ).child( objectAttributes[ "XML" ].@archetype ) ).toXMLString() );
			objectAttributes[ "XMLArchetype" ].ignoreWhitespace = true;
			
			// Initialize Listeners
			addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
		}
		
		public function onAddedToStage( tEvent:Event ):void
		{
			initObject();
			delete objectAttributes[ "XML" ];
			delete objectAttributes[ "XMLArchetype" ];
			
			// Set Listeners
			addEventListener( Event.REMOVED_FROM_STAGE, onRemovedFromStage );
			removeEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
		}
		
		public function onRemovedFromStage( tEvent:Event ):void
		{
			// Remove Listeners
			removeEventListener( Event.REMOVED_FROM_STAGE, onRemovedFromStage );
						
			// Clear arrays
			clearMemory( objectAttributes );
			clearMemory( objectComponents );
		}
		
		//=========================
		// Mutators
		//=========================			
		public function getComponent( tWhat:String ):*
		{
			return objectComponents[ tWhat ];
		}
		
		public function setComponent( tWhat:String, tValue:* = null, tBeforeComponent:String = "" ):void
		{
			if ( objectAttributes[ "ComponentPriority" ] == null )
			{
				objectAttributes[ "ComponentPriority" ] = new Vector.<String>;
			}
			
			if ( tValue != null )
			{
				if ( tBeforeComponent != "" )
				{
					objectAttributes[ "ComponentPriority" ].splice( objectAttributes[ "ComponentPriority" ].indexOf( tBeforeComponent ), 0, tWhat );
				}
				else
				{
					objectAttributes[ "ComponentPriority" ].push( tWhat );
				}
				
				objectComponents[ tWhat ] = tValue;
				objectComponents[ tWhat ].initComponent( game, this );
			}
			else
			{
				if ( objectComponents[ tWhat ] != null )
				{
					objectAttributes[ "ComponentPriority" ].splice( objectAttributes[ "ComponentPriority" ].indexOf( tWhat ), 1 );
					objectComponents[ tWhat ].removeComponent( game, this );
					delete objectComponents[ tWhat ];
				}
			}
		}
		
		//=========================
		// Members
		//=========================
		protected function initObject():void
		{
			// Load Basics
			loadObject( objectAttributes[ "XMLArchetype" ], "Basic" );
			loadObject( objectAttributes[ "XML" ], "Basic" );
			
			// Load Components
			loadObject( objectAttributes[ "XMLArchetype" ], "Components" );
			loadObject( objectAttributes[ "XML" ], "Components" );
			
			// Load Attributes
			loadObject( objectAttributes[ "XMLArchetype" ], "Attributes" );
			loadObject( objectAttributes[ "XML" ], "Attributes" );
			
			// Load Physics
			loadObject( objectAttributes[ "XMLArchetype" ], "Physics" );
			loadObject( objectAttributes[ "XML" ], "Physics" );
		}
		
		protected function loadObject( tXML:XML, tWhat:String ):void
		{
			if ( tXML != null )
			{
				// Temp Variables
				var tempItem:XML;
				var tempItemTwo:XML;
				var tempShape:b2Shape;
				
				// Determine Type
				switch( tWhat )
				{
					case "Basic":
						// Set Basics
						for each ( tempItem in ( tXML.Basic ).children() )
						{
							switch( new String( tempItem.name() ) )
							{
								case "Location":
									x = tempItem.@x;
									y = tempItem.@y;
									break;
								case "Scale":
									scaleX = tempItem.@x;
									scaleY = tempItem.@y;
									break;
								case "Pivot":
									pivotX = tempItem.@x;
									pivotY = tempItem.@y;
									break;
								case "Rotation":
									rotation = tempItem.@rotation;
									break;
								case "Size":
									width = tempItem.@width;
									height = tempItem.@height;
									break;
								case "Blend":
									alpha = tempItem.@alpha;
									blendMode = tempItem.@mode;
									break;
								case "Visible":
									visible = tempItem.@visible;
									break;
								default:
									break;
							}
						}
						break;
					case "Components":
						// Set Components
						for each ( tempItem in ( tXML.Components ).Component )
						{
							switch( new String( tempItem.@type ) )
							{
								case "AIHoming":
									setComponent( new String( tempItem.@name ), new AIHoming() );
									break;
								case "AIWaypoint":
									setComponent( new String( tempItem.@name ), new AIWaypoint() );
									break;
								case "AnimationDeath":
									setComponent( new String( tempItem.@name ), new AnimationDeath() );
									break;
								case "AnimationCharacter":
									setComponent( new String( tempItem.@name ), new AnimationCharacter() );
									break;
								case "AnimationCharacterIdle":
									setComponent( new String( tempItem.@name ), new AnimationCharacterIdle() );
									break;
								case "CollisionAIPatrol":
									setComponent( "Collision", new CollisionAIPatrol() )
									break;
								case "CollisionPickup":
									setComponent( "Collision", new CollisionPickup() );
									break;
								case "GeneralModifier":
									setComponent( new String( tempItem.@name ), new GeneralModifier() );
									break;
								case "GeneralSpin":
									setComponent( new String( tempItem.@name ), new GeneralSpin() );
									break;
								case "InputBase":
									setComponent( new String( tempItem.@name ), new InputBase() );
									break;
								case "LevelPickup":
									setComponent( new String( tempItem.@name ), new LevelPickup() );
									break;
								case "LevelPickupAnimated":
									setComponent( new String( tempItem.@name ), new LevelPickupAnimated() );
									break;
								case "PhysicsThrust":
									setComponent( new String( tempItem.@name ), new PhysicsThrust() );
									break;
								case "WillardPlayer":
									setComponent( new String( tempItem.@name ), new WillardPlayer() );
									break;
								case "WillardInput":
									setComponent( new String( tempItem.@name ), new WillardInput() );
									break;
								case "WillardCollisionPlayer":
									setComponent( "Collision", new WillardCollisionPlayer() )
									break;
								default:
									break;
							}
						}
						break;
					case "Attributes":
						// Set Attributes
						for each ( tempItem in ( tXML.Attributes ).Attribute )
						{
							switch( new String( ( tempItem.children()[0] ).name() ) )
							{
								// Basic Types
								case "Point":
									setAttribute( new String( tempItem.@name ), new PointSimple( ( tempItem.children()[0] ).@x, ( tempItem.children()[0] ).@y ) );
									break;
								case "Boolean":
									setAttribute( new String( tempItem.@name ), new Boolean( new int( ( tempItem.children()[0] ).@value ) ) );
									break;
								case "Number":
									setAttribute( new String( tempItem.@name ), new Number( ( tempItem.children()[0] ).@value ) );
									break;
								case "Uint":
									setAttribute( new String( tempItem.@name ), new uint( ( tempItem.children()[0] ).@value ) );
									break;
								case "Int":
									setAttribute( new String( tempItem.@name ), new int( ( tempItem.children()[0] ).@value ) );
									break;
								case "Vector":
									setAttribute( new String( tempItem.@name ), new b2Vec2( ( tempItem.children()[0] ).@x, ( tempItem.children()[0] ).@y ) );
									break;
								case "String":
									setAttribute( new String( tempItem.@name ), new String( ( tempItem.children()[0] ).@value ) );
									break;
								// Vecor Lists
								case "List":
									switch( new String( ( tempItem.children()[0] ).@type ) )
									{
										case "Point":
											setAttribute( new String( tempItem.@name ), new Vector.<PointSimple> );
											
											for each ( tempItemTwo in ( tempItem.children()[0] ).children() )
											{
												if ( tempItemTwo.name() == new String( ( tempItem.children()[0] ).@type ) )
												{
													getAttribute( new String( tempItem.@name ) ).push( new PointSimple( tempItemTwo.@x, tempItemTwo.@y ) );
												}
											}
											break;
										case "Boolean":
											setAttribute( new String( tempItem.@name ), new Vector.<Boolean> );
											
											for each ( tempItemTwo in ( tempItem.children()[0] ).children() )
											{
												if ( tempItemTwo.name() == new String( ( tempItem.children()[0] ).@type ) )
												{
													getAttribute( new String( tempItem.@name ) ).push( new Boolean( new int( tempItemTwo.@value ) ) );
												}
											}
											break;
										case "Number":
											setAttribute( new String( tempItem.@name ), new Vector.<Number> );
											
											for each ( tempItemTwo in ( tempItem.children()[0] ).children() )
											{
												if ( tempItemTwo.name() == new String( ( tempItem.children()[0] ).@type ) )
												{
													getAttribute( new String( tempItem.@name ) ).push( new Number( tempItemTwo.@value ) );
												}
											}
											break;
										case "Uint":
											setAttribute( new String( tempItem.@name ), new Vector.<uint> );
											
											for each ( tempItemTwo in ( tempItem.children()[0] ).children() )
											{
												if ( tempItemTwo.name() == new String( ( tempItem.children()[0] ).@type ) )
												{
													getAttribute( new String( tempItem.@name ) ).push( new uint( tempItemTwo.@value ) );
												}
											}
											break;
										case "Int":
											setAttribute( new String( tempItem.@name ), new Vector.<int> );
											
											for each ( tempItemTwo in ( tempItem.children()[0] ).children() )
											{
												if ( tempItemTwo.name() == new String( ( tempItem.children()[0] ).@type ) )
												{
													getAttribute( new String( tempItem.@name ) ).push( new int( tempItemTwo.@value ) );
												}
											}
											break;
										case "Vector":
											setAttribute( new String( tempItem.@name ), new Vector.<b2Vec2> );
											
											for each ( tempItemTwo in ( tempItem.children()[0] ).children() )
											{
												if ( tempItemTwo.name() == new String( ( tempItem.children()[0] ).@type ) )
												{
													getAttribute( new String( tempItem.@name ) ).push( new b2Vec2( tempItemTwo.@x, tempItemTwo.@y ) );
												}
											}
											break;
										case "String":
											setAttribute( new String( tempItem.@name ), new Vector.<String> );
											
											for each ( tempItemTwo in ( tempItem.children()[0] ).children() )
											{
												if ( tempItemTwo.name() == new String( ( tempItem.children()[0] ).@type ) )
												{
													getAttribute( new String( tempItem.@name ) ).push( new String( tempItemTwo.@value ) );
												}
											}
											break;
										default:
											break;
									}
									break;
								// Graphics
								case "GraphicBasic":
									setAttribute( new String( tempItem.@name ), new GraphicsBasic( ( game.gameAssets ).getAsset( new String( ( tempItem.children()[0] ).@texture ) ), new Boolean( new int( ( tempItem.children()[0] ).@visible ) ), new PointSimple( ( tempItem.children()[0] ).@x, ( tempItem.children()[0] ).@y ), new PointSimple( ( tempItem.children()[0] ).@localx, ( tempItem.children()[0] ).@localy ), new PointSimple( ( tempItem.children()[0] ).@pivotx, ( tempItem.children()[0] ).@pivoty ) ) );
																		
									// Merged graphics
									for each ( tempItemTwo in ( tempItem.children()[0] ).children() )
									{
										if ( tempItemTwo.name() == "Image" )
										{
											getAttribute( new String( tempItem.@name ) ).addGraphic( ( game.gameAssets ).getAsset( new String( tempItemTwo.@texture ) ), new PointSimple( tempItemTwo.@localx, tempItemTwo.@localy ) );
										}
									}
									
									addChild( getAttribute( new String( tempItem.@name ) ) );
									
									if ( new Boolean( tempItem.@flatten ) )
									{
										getAttribute( new String( tempItem.@name ) ).flattenGraphic( true );
									}
									break;
								case "GraphicTiled":
									setAttribute( new String( tempItem.@name ), new GraphicsTiled( ( game.gameAssets ).getAsset( new String( ( tempItem.children()[0] ).@texture ) ), new Boolean( new int( ( tempItem.children()[0] ).@visible ) ), new PointSimple( ( tempItem.children()[0] ).@x, ( tempItem.children()[0] ).@y ), new PointSimple( ( tempItem.children()[0] ).@localx, ( tempItem.children()[0] ).@localy ), new PointSimple( ( tempItem.children()[0] ).@pivotx, ( tempItem.children()[0] ).@pivoty ), new PointSimple( ( tempItem.children()[0] ).@repeatx, ( tempItem.children()[0] ).@repeaty ), new PointSimple( ( tempItem.children()[0] ).@scalex, ( tempItem.children()[0] ).@scaley ) ) );
									
									// Merged graphics
									for each ( tempItemTwo in ( tempItem.children()[0] ).children() )
									{
										if ( tempItemTwo.name() == "Image" )
										{
											getAttribute( new String( tempItem.@name ) ).addGraphic( ( game.gameAssets ).getAsset( new String( tempItemTwo.@texture ) ), new PointSimple( tempItemTwo.@localx, tempItemTwo.@localy ), new PointSimple( tempItemTwo.@repeatx, tempItemTwo.@repeaty ), new PointSimple( tempItemTwo.@scalex, tempItemTwo.@scaley ) );
										}
									}
									
									addChild( getAttribute( new String( tempItem.@name ) ) );
									
									if ( new Boolean( tempItem.@flatten ) )
									{
										getAttribute( new String( tempItem.@name ) ).flattenGraphic( true );
									}
									break;
								case "GraphicMovie":								
									setAttribute( new String( tempItem.@name ), new GraphicsMovie( ( game.gameAssets ).getAsset( new String( ( tempItem.children()[0] ).@movielist ) ), new Boolean( new int( ( tempItem.children()[0] ).@visible ) ), new PointSimple( ( tempItem.children()[0] ).@x, ( tempItem.children()[0] ).@y ), new PointSimple( ( tempItem.children()[0] ).@pivotx, ( tempItem.children()[0] ).@pivoty ), new uint( ( tempItem.children()[0] ).@index ), new Boolean( new int( ( tempItem.children()[0] ).@looping ) ), new Boolean( new int( ( tempItem.children()[0] ).@paused ) ) ) );
									addChild( getAttribute( new String( tempItem.@name ) ) );
									
									if ( getAttribute( "ObjectMovies" ) == null )
									{
										setAttribute( "ObjectMovies", new Vector.<String> );
									}
									getAttribute( "ObjectMovies" ).push( new String( tempItem.@name ) );
									break;
								case "GraphicFlipbook":
									setAttribute( new String( tempItem.@name ), new GraphicsFlipbook( ( game.gameAssets ).getAsset( new String( ( tempItem.children()[0] ).@texturelist ) ), new Boolean( new int( ( tempItem.children()[0] ).@visible ) ), new PointSimple( ( tempItem.children()[0] ).@x, ( tempItem.children()[0] ).@y ), new PointSimple( ( tempItem.children()[0] ).@pivotx, ( tempItem.children()[0] ).@pivoty ), new uint( ( tempItem.children()[0] ).@index ) ) );
									addChild( getAttribute( new String( tempItem.@name ) ) );
									break;
								case "TextField":
									setAttribute( new String( tempItem.@name ), new TextField( new int( ( tempItem.children()[0] ).@width ), new int( ( tempItem.children()[0] ).@height ), new String( ( tempItem.children()[0] ).@text ), new String( ( tempItem.children()[0] ).@font ), new Number( ( tempItem.children()[0] ).@fontsize ), new uint( ( tempItem.children()[0] ).@color ), new Boolean( ( tempItem.children()[0] ).@bold ) ) );
									getAttribute( new String( tempItem.@name ) ).hAlign = ( tempItem.children()[0] ).@halign;
									getAttribute( new String( tempItem.@name ) ).x = ( tempItem.children()[0] ).@x;
									getAttribute( new String( tempItem.@name ) ).y = ( tempItem.children()[0] ).@y;
									getAttribute( new String( tempItem.@name ) ).visible = new Boolean( new int( ( tempItem.children()[0] ).@visible ) );
									addChild( getAttribute( new String( tempItem.@name ) ) );
									break;
								default:
									break;
							}
						}
						break;
					case "Physics":
						// Set Physics
						if ( tXML.Physics != undefined )
						{
							setAttribute( "Physics", ( game.gameLevel ).createBodyPhysics( ( game.gameLevel ).getAttribute( "PhysicsEngine" ), ( game.gameLevel ).getAttribute( "PhysicsDefaultPixelPerMeter" ), this, new PointSimple( ( tXML.Physics ).@x, ( tXML.Physics ).@y ), new uint( ( tXML.Physics ).@type ), new Boolean( new int( ( tXML.Physics ).@fixedrotation ) ), new Number( ( tXML.Physics ).@lineardamping ) ) );
							setAttribute( "CollisionID", new String( ( tXML.Physics ).@id ) );
							
							// Fixtures
							for each ( tempItem in ( tXML.Physics ).children() )
							{								
								// Shape
								switch( new String( ( tempItem.children()[0] ).name() ) )
								{
									case "Circle":
										tempShape = ( game.gameLevel ).createCirclePhysics( ( game.gameLevel ).getAttribute( "PhysicsDefaultPixelPerMeter" ), new Number( ( tempItem.children()[0] ).@radius ), new PointSimple( ( tempItem.children()[0] ).@localx, ( tempItem.children()[0] ).@localy ) );
										break;
									case "Box":
										tempShape = ( game.gameLevel ).createBoxPhysics( ( game.gameLevel ).getAttribute( "PhysicsDefaultPixelPerMeter" ), new PointSimple( ( tempItem.children()[0] ).@localx, ( tempItem.children()[0] ).@localy ), new PointSimple( ( tempItem.children()[0] ).@width, ( tempItem.children()[0] ).@height ) );
										break;
									case "BoxRotated":
										tempShape = ( game.gameLevel ).createBoxRotatedPhysics( ( game.gameLevel ).getAttribute( "PhysicsDefaultPixelPerMeter" ), new PointSimple( ( tempItem.children()[0] ).@width, ( tempItem.children()[0] ).@height ), new Number( ( tempItem.children()[0] ).@rotation ), new PointSimple( ( tempItem.children()[0] ).@pivotx, ( tempItem.children()[0] ).@pivoty ) );
										break;
									default:
										break;
								}
								
								getAttribute( "Physics" ).CreateFixture( ( game.gameLevel ).createFixturePhysics( tempShape, new String( tempItem.@id ), new uint( tempItem.@category ), new Number( tempItem.@density ), new Number( tempItem.@friction ), new Number( tempItem.@restitution ), new Boolean( new int( tempItem.@sensor ) ) ) );
							}
							break;
						}
						break;
					default:
						break;
				}
			}
		}
		
		public function updateObject( tLevel:GameLevel = null ):void
		{
			if ( getAttribute( "ComponentPriority" ) != null )
			{
				var tempListLength:uint = getAttribute( "ComponentPriority" ).length;
				for ( var i:uint = 0; i < tempListLength; i++ )
				{
					objectComponents[ getAttribute( "ComponentPriority" )[i] ].updateComponent( game, this, tLevel );
				}
			}
		}
		
		public function pauseObject( tIsPaused:Boolean ):void
		{
			// Toggle Movies
			if ( getAttribute( "ObjectMovies" ) != null )
			{
				var tempListLength:uint = getAttribute( "ObjectMovies" ).length;
				for ( var i:uint = 0; i < tempListLength; i++ )
				{
					getAttribute( getAttribute( "ObjectMovies" )[i] ).controlGraphic( 0, tIsPaused );
				}
			}
		}
		
		protected function clearMemory( tDictionary:Dictionary ):void
		{
			for ( var tempKey:String in tDictionary )
			{
				delete tDictionary[ tempKey ];
			}
		}
	}
}